home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / gz.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-09-12  |  15.5 KB  |  609 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Daniel Risacher, magnus@alum.mit.edu
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. /* Minor changes to support file magic */
  21. /* 4 Oct 1997 -- Risacher */
  22.  
  23. /* gzip plug-in for the gimp */
  24. /* loosley based on url.c by */
  25. /* Josh MacDonald, jmacd@cs.berkeley.edu */
  26.  
  27. /* and, very loosely on hrz.c by */
  28. /* Albert Cahalan <acahalan at cs.uml.edu> */
  29.  
  30. /* This is reads and writes gziped image files for the Gimp
  31.  *
  32.  * You need to have gzip installed for it to work.
  33.  *
  34.  * It should work with file names of the form
  35.  * filename.foo.gz where foo is some already-recognized extension
  36.  *
  37.  * and it also works for names of the form
  38.  * filename.xcfgz - which is equivalent to
  39.  * filename.xcf.gz
  40.  *
  41.  * I added the xcfgz bit because having a default extension of xcf.gz
  42.  * can confuse the file selection dialog box somewhat, forcing the
  43.  * user to type sometimes when he/she otherwise wouldn't need to.
  44.  *
  45.  * I later decided I didn't like it because I don't like to bloat
  46.  * the file-extension namespace.  But I left in the recognition
  47.  * feature/bug so if people want to use files named foo.xcfgz by
  48.  * default, they can just hack their pluginrc file.
  49.  *
  50.  * to do this hack, change :
  51.  *                      "xcf.gz,gz,xcfgz"
  52.  * to
  53.  *                      "xcfgz,gz,xcf.gz"
  54.  *
  55.  *
  56.  * -Dan Risacher, 0430 CDT, 26 May 1997
  57.  */
  58.  
  59. #include "config.h"
  60.  
  61. #include <stdlib.h>
  62. #include <stdio.h>
  63. #include <sys/types.h>
  64. #ifdef HAVE_SYS_PARAM_H
  65. #include <sys/param.h>
  66. #endif
  67. #ifdef HAVE_SYS_WAIT_H
  68. #include <sys/wait.h>
  69. #endif
  70. #include <sys/stat.h>
  71. #include <string.h>
  72. #ifdef HAVE_UNISTD_H
  73. #include <unistd.h>
  74. #endif
  75. #include <errno.h>
  76.  
  77. #ifdef __EMX__
  78. #include <fcntl.h>
  79. #include <process.h>
  80. #endif
  81.  
  82. #include <libgimp/gimp.h>
  83.  
  84. #ifdef G_OS_WIN32
  85. #define STRICT
  86. #define WinMain WinMain_foo
  87. #include <windows.h>
  88. #undef WinMain
  89. #endif
  90.  
  91. #include "libgimp/stdplugins-intl.h"
  92.  
  93.  
  94. /* Author 1: Josh MacDonald (url.c) */
  95. /* Author 2: Daniel Risacher (gz.c) */
  96.  
  97. /* According to USAF Lt Steve Werhle, US DoD software development
  98.  * contracts average about $25 USD per source line of code (SLOC).  By
  99.  * that metric, I figure this plug-in is worth about $10,000 USD */
  100. /* But you got it free.   Magic of Gnu. */
  101.  
  102.  
  103. static void          query       (void);
  104. static void          run         (gchar       *name,
  105.                   gint         nparams,
  106.                   GimpParam      *param,
  107.                   gint        *nreturn_vals,
  108.                   GimpParam     **return_vals);
  109.  
  110. static gint32        load_image  (gchar       *filename,
  111.                   gint32       run_mode,
  112.                   GimpPDBStatusType *status /* return value */);
  113. static GimpPDBStatusType   save_image  (gchar       *filename,
  114.                   gint32       image_ID,
  115.                   gint32       drawable_ID,
  116.                   gint32       run_mode);
  117.  
  118. static gboolean   valid_file     (gchar       *filename);
  119. static gchar    * find_extension (gchar       *filename);
  120.  
  121. GimpPlugInInfo PLUG_IN_INFO =
  122. {
  123.   NULL,  /* init_proc  */
  124.   NULL,  /* quit_proc  */
  125.   query, /* query_proc */
  126.   run,   /* run_proc   */
  127. };
  128.  
  129. MAIN ()
  130.  
  131. static void
  132. query (void)
  133. {
  134.   static GimpParamDef load_args[] =
  135.   {
  136.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  137.     { GIMP_PDB_STRING, "filename", "The name of the file to load" },
  138.     { GIMP_PDB_STRING, "raw_filename", "The name entered" }
  139.   };
  140.   static GimpParamDef load_return_vals[] =
  141.   {
  142.     { GIMP_PDB_IMAGE, "image", "Output image" },
  143.   };
  144.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  145.   static gint nload_return_vals = (sizeof (load_return_vals) /
  146.                    sizeof (load_return_vals[0]));
  147.  
  148.   static GimpParamDef save_args[] =
  149.   {
  150.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  151.     { GIMP_PDB_IMAGE, "image", "Input image" },
  152.     { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
  153.     { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
  154.     { GIMP_PDB_STRING, "raw_filename", "The name of the file to save the image in" }
  155.   };
  156.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  157.  
  158.   gimp_install_procedure ("file_gz_load",
  159.                           "loads files compressed with gzip",
  160.                           "You need to have gzip installed.",
  161.                           "Daniel Risacher",
  162.                           "Daniel Risacher, Spencer Kimball and Peter Mattis",
  163.                           "1995-1997",
  164.                           "<Load>/gzip",
  165.               NULL,
  166.                           GIMP_PLUGIN,
  167.                           nload_args, nload_return_vals,
  168.                           load_args, load_return_vals);
  169.  
  170.   gimp_install_procedure ("file_gz_save",
  171.                           "saves files compressed with gzip",
  172.                           "You need to have gzip installed.",
  173.                           "Daniel Risacher",
  174.                           "Daniel Risacher, Spencer Kimball and Peter Mattis",
  175.                           "1995-1997",
  176.                           "<Save>/gzip",
  177.               "RGB*, GRAY*, INDEXED*",
  178.                           GIMP_PLUGIN,
  179.                           nsave_args, 0,
  180.                           save_args, NULL);
  181.  
  182.   gimp_register_magic_load_handler ("file_gz_load",
  183.                     "xcf.gz,gz,xcfgz", 
  184.                     "",
  185.                     "0,string,\037\213");
  186.   gimp_register_save_handler       ("file_gz_save",
  187.                     "xcf.gz,gz,xcfgz",
  188.                     "");
  189. }
  190.  
  191. static void
  192. run (gchar   *name,
  193.      gint     nparams,
  194.      GimpParam  *param,
  195.      gint    *nreturn_vals,
  196.      GimpParam **return_vals)
  197. {
  198.   static GimpParam values[2];
  199.   GimpRunModeType  run_mode;
  200.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  201.   gint32        image_ID;
  202.  
  203.   run_mode = param[0].data.d_int32;
  204.  
  205.   INIT_I18N();
  206.  
  207.   *nreturn_vals = 1;
  208.   *return_vals  = values;
  209.   values[0].type          = GIMP_PDB_STATUS;
  210.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  211.  
  212.   if (strcmp (name, "file_gz_load") == 0)
  213.     {
  214.       image_ID = load_image (param[1].data.d_string,
  215.                  param[0].data.d_int32,
  216.                  &status);
  217.  
  218.       if (image_ID != -1 &&
  219.       status == GIMP_PDB_SUCCESS)
  220.     {
  221.       *nreturn_vals = 2;
  222.       values[1].type         = GIMP_PDB_IMAGE;
  223.       values[1].data.d_image = image_ID;
  224.     }
  225.     }
  226.   else if (strcmp (name, "file_gz_save") == 0)
  227.     {
  228.       switch (run_mode)
  229.     {
  230.     case GIMP_RUN_INTERACTIVE:
  231.       break;
  232.     case GIMP_RUN_NONINTERACTIVE:
  233.       /*  Make sure all the arguments are there!  */
  234.       if (nparams != 4)
  235.         status = GIMP_PDB_CALLING_ERROR;
  236.       break;
  237.     case GIMP_RUN_WITH_LAST_VALS:
  238.       break;
  239.  
  240.     default:
  241.       break;
  242.     }
  243.  
  244.       if (status == GIMP_PDB_SUCCESS)
  245.     {
  246.       status = save_image (param[3].data.d_string,
  247.                    param[1].data.d_int32,
  248.                    param[2].data.d_int32,
  249.                    param[0].data.d_int32);
  250.      }
  251.     }
  252.   else
  253.     {
  254.       status = GIMP_PDB_CALLING_ERROR;
  255.     }
  256.  
  257.   values[0].data.d_status = status;
  258. }
  259.  
  260. #ifdef __EMX__
  261. static gint
  262. spawn_gzip (gchar *filename,
  263.         gchar *tmpname,
  264.         gchar *parms,
  265.         gint  *pid)
  266. {
  267.   FILE *f;
  268.   gint tfd;
  269.   
  270.   if (!(f = fopen (filename,"w")))
  271.     {
  272.       g_message ("gz: fopen failed: %s\n", g_strerror (errno));
  273.       return -1;
  274.     }
  275.  
  276.   /* save fileno(stdout) */
  277.   tfd = dup (fileno (stdout));
  278.   /* make stdout for this process be the output file */
  279.   if (dup2 (fileno (f), fileno (stdout)) == -1)
  280.     {
  281.       g_message ("gz: dup2 failed: %s\n", g_strerror (errno));
  282.       close (tfd);
  283.       return -1;
  284.     }
  285.   fcntl (tfd, F_SETFD, FD_CLOEXEC);
  286.   *pid = spawnlp (P_NOWAIT, "gzip", "gzip", parms, tmpname, NULL);
  287.   fclose (f);
  288.   /* restore fileno(stdout) */
  289.   dup2 (tfd, fileno (stdout));
  290.   close (tfd);
  291.   if (*pid == -1)
  292.     {
  293.       g_message ("gz: spawn failed: %s\n", g_strerror (errno));
  294.       return -1;
  295.     }
  296.   return 0;
  297. }
  298. #endif
  299.  
  300. static GimpPDBStatusType
  301. save_image (gchar  *filename,
  302.         gint32  image_ID,
  303.         gint32  drawable_ID,
  304.         gint32  run_mode)
  305. {
  306.   gchar *ext;
  307.   gchar *tmpname;
  308. #ifndef G_OS_WIN32
  309.   FILE  *f;
  310.   gint   pid;
  311.   gint   wpid;
  312.   gint   process_status;
  313. #else
  314.   FILE  *in, *out;
  315.   STARTUPINFO         startupinfo;
  316.   PROCESS_INFORMATION processinfo;
  317. #endif
  318.  
  319.   if (NULL == (ext = find_extension (filename)))
  320.     {
  321.       g_message (_("gz: no sensible extension, saving as gzip'd xcf\n"));
  322.       ext = ".xcf";
  323.     }
  324.  
  325.   /* get a temp name with the right extension and save into it. */
  326.  
  327.   tmpname = gimp_temp_name (ext + 1);
  328.  
  329.   if (! (gimp_file_save (run_mode,
  330.              image_ID,
  331.              drawable_ID,
  332.              tmpname, 
  333.              tmpname) && valid_file (tmpname)) )
  334.     {
  335.       unlink (tmpname);
  336.       g_free (tmpname);
  337.       return GIMP_PDB_EXECUTION_ERROR;
  338.     }
  339.  
  340. #ifndef G_OS_WIN32
  341. #ifdef __EMX__
  342.    if (spawn_gzip (filename, tmpname, "-cf", &pid) == -1)  
  343.     {
  344.       g_free (tmpname);
  345.       return GIMP_PDB_EXECUTION_ERROR;
  346.     }
  347.   sleep (2); /* silly wait */
  348. #else /* UNIX */
  349.   /* fork off a gzip process */
  350.   if ((pid = fork ()) < 0)
  351.     {
  352.       g_message ("gz: fork failed: %s\n", g_strerror (errno));
  353.       g_free (tmpname);
  354.       return GIMP_PDB_EXECUTION_ERROR;
  355.     }
  356.   else if (pid == 0)
  357.     {
  358.  
  359.       if (!(f = fopen (filename, "w")))
  360.     {
  361.       g_message ("gz: fopen failed: %s\n", g_strerror (errno));
  362.       g_free (tmpname);
  363.       _exit (127);
  364.     }
  365.  
  366.       /* make stdout for this process be the output file */
  367.       if (-1 == dup2 (fileno (f), fileno (stdout)))
  368.     g_message ("gz: dup2 failed: %s\n", g_strerror (errno));
  369.  
  370.       /* and gzip into it */
  371.       execlp ("gzip", "gzip", "-cf", tmpname, NULL);
  372.       g_message ("gz: exec failed: gzip: %s\n", g_strerror (errno));
  373.       g_free (tmpname);
  374.       _exit(127);
  375.     }
  376.   else
  377.     {
  378.       wpid = waitpid (pid, &process_status, 0);
  379.  
  380.       if ((wpid < 0)
  381.       || !WIFEXITED (process_status)
  382.       || (WEXITSTATUS (process_status) != 0))
  383.     {
  384.       g_message ("gz: gzip exited abnormally on file %s\n", tmpname);
  385.       g_free (tmpname);
  386.       return 0;
  387.     }
  388.     }
  389. #endif
  390. #else  /* G_OS_WIN32 */
  391.   in = fopen (tmpname, "rb");
  392.   out = fopen (filename, "wb");
  393.  
  394.   startupinfo.cb = sizeof (STARTUPINFO);
  395.   startupinfo.lpReserved = NULL;
  396.   startupinfo.lpDesktop = NULL;
  397.   startupinfo.lpTitle = NULL;
  398.   startupinfo.dwFlags =
  399.     STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  400.   startupinfo.wShowWindow = SW_SHOWMINNOACTIVE;
  401.   startupinfo.cbReserved2 = 0;
  402.   startupinfo.lpReserved2 = NULL;
  403.   startupinfo.hStdInput = (HANDLE) _get_osfhandle (fileno (in));
  404.   startupinfo.hStdOutput = (HANDLE) _get_osfhandle (fileno (out));
  405.   startupinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
  406.  
  407.   if (!CreateProcess (NULL, "minigzip", NULL, NULL,
  408.               TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL,
  409.               &startupinfo, &processinfo))
  410.     {
  411.       g_message ("gz: CreateProcess failed. Minigzip.exe not installed or not in PATH?\n");
  412.       unlink (tmpname);
  413.       g_free (tmpname);
  414.       return GIMP_PDB_EXECUTION_ERROR;
  415.     }
  416.   CloseHandle (processinfo.hThread);
  417.   WaitForSingleObject (processinfo.hProcess, INFINITE);
  418.   fclose (in);
  419.   fclose (out);
  420. #endif /* G_OS_WIN32 */
  421.  
  422.   unlink (tmpname);
  423.   g_free (tmpname);
  424.  
  425.   return GIMP_PDB_SUCCESS;
  426. }
  427.  
  428. static gint32
  429. load_image (gchar             *filename,
  430.         gint32             run_mode,
  431.         GimpPDBStatusType *status /* return value */)
  432. {
  433.   gint32  image_ID;
  434.   gchar  *ext;
  435.   gchar  *tmpname;
  436. #ifndef G_OS_WIN32
  437.   gint    pid;
  438.   gint    wpid;
  439.   gint    process_status;
  440. #else
  441.   FILE   *in, *out;
  442.   STARTUPINFO         startupinfo;
  443.   PROCESS_INFORMATION processinfo;
  444. #endif
  445.  
  446.   if (NULL == (ext = find_extension (filename)))
  447.     {
  448.       g_message (_("gz: no sensible extension, "
  449.                    "attempting to load with file magic\n"));
  450.       ext = ".foo";
  451.     }
  452.  
  453.   /* find a temp name */
  454.   tmpname = gimp_temp_name (ext + 1);
  455.  
  456. #ifndef G_OS_WIN32  
  457. #ifdef __EMX__
  458.    if (spawn_gzip (tmpname, filename, "-cfd", &pid) == -1)
  459.      {
  460.        g_free (tmpname);
  461.        *status = GIMP_PDB_EXECUTION_ERROR;
  462.        return -1;
  463.      }
  464. #else /* UNIX */
  465. /* fork off a g(un)zip and wait for it */
  466.   if ((pid = fork ()) < 0)
  467.     {
  468.       g_message ("gz: fork failed: %s\n", g_strerror (errno));
  469.       g_free (tmpname);
  470.       *status = GIMP_PDB_EXECUTION_ERROR;
  471.       return -1;
  472.     }
  473.   else if (pid == 0)  /* child process */
  474.     {
  475.       FILE *f;
  476.        if (!(f = fopen (tmpname, "w")))
  477.      {
  478.        g_message ("gz: fopen failed: %s\n", g_strerror (errno));
  479.        g_free (tmpname);
  480.        _exit(127);
  481.      }
  482.  
  483.       /* make stdout for this child process be the temp file */
  484.       if (-1 == dup2 (fileno (f), fileno (stdout)))
  485.     {
  486.       g_free (tmpname);
  487.       g_message ("gz: dup2 failed: %s\n", g_strerror (errno));
  488.     }
  489.  
  490.       /* and unzip into it */
  491.       execlp ("gzip", "gzip", "-cfd", filename, NULL);
  492.       g_message ("gz: exec failed: gunzip: %s\n", g_strerror (errno));
  493.       g_free (tmpname);
  494.       _exit(127);
  495.     }
  496.   else  /* parent process */
  497.     {
  498.       wpid = waitpid (pid, &process_status, 0);
  499.  
  500.       if ((wpid < 0)
  501.       || !WIFEXITED (process_status)
  502.       || (WEXITSTATUS (process_status) != 0))
  503.     {
  504.       g_message ("gz: gzip exited abnormally on file %s\n", filename);
  505.       g_free (tmpname);
  506.       *status = GIMP_PDB_EXECUTION_ERROR;
  507.       return -1;
  508.     }
  509.     }
  510. #endif
  511. #else  /* G_OS_WIN32 */
  512.   in = fopen (filename, "rb");
  513.   out = fopen (tmpname, "wb");
  514.  
  515.   startupinfo.cb = sizeof (STARTUPINFO);
  516.   startupinfo.lpReserved = NULL;
  517.   startupinfo.lpDesktop = NULL;
  518.   startupinfo.lpTitle = NULL;
  519.   startupinfo.dwFlags =
  520.     STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  521.   startupinfo.wShowWindow = SW_SHOWMINNOACTIVE;
  522.   startupinfo.cbReserved2 = 0;
  523.   startupinfo.lpReserved2 = NULL;
  524.   startupinfo.hStdInput = (HANDLE) _get_osfhandle (fileno (in));
  525.   startupinfo.hStdOutput = (HANDLE) _get_osfhandle (fileno (out));
  526.   startupinfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
  527.  
  528.   if (!CreateProcess (NULL, "minigzip -d", NULL, NULL,
  529.               TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL,
  530.               &startupinfo, &processinfo))
  531.     {
  532.       g_message ("gz: CreateProcess failed. Minigzip.exe not installed or not in PATH?\n");
  533.       g_free (tmpname);
  534.       *status = GIMP_PDB_EXECUTION_ERROR;
  535.       return -1;
  536.     }
  537.   CloseHandle (processinfo.hThread);
  538.   WaitForSingleObject (processinfo.hProcess, INFINITE);
  539.   fclose (in);
  540.   fclose (out);
  541. #endif /* G_OS_WIN32 */
  542.  
  543.   /* now that we un-gziped it, load the temp file */
  544.  
  545.   image_ID = gimp_file_load (run_mode, tmpname, tmpname);
  546.  
  547.   unlink (tmpname);
  548.   g_free (tmpname);
  549.  
  550.   if (image_ID != -1)
  551.     {
  552.       *status = GIMP_PDB_SUCCESS;
  553.       gimp_image_set_filename (image_ID, filename);
  554.     }
  555.   else
  556.     *status = GIMP_PDB_EXECUTION_ERROR;
  557.  
  558.   return image_ID;
  559. }
  560.  
  561. static gboolean
  562. valid_file (gchar *filename)
  563. {
  564.   gint stat_res;
  565.   struct stat buf;
  566.  
  567.   stat_res = stat(filename, &buf);
  568.  
  569.   if ((0 == stat_res) && (buf.st_size > 0))
  570.     return TRUE;
  571.   else
  572.     return FALSE;
  573. }
  574.  
  575. static gchar *
  576. find_extension (gchar *filename)
  577. {
  578.   gchar *filename_copy;
  579.   gchar *ext;
  580.  
  581.   /* we never free this copy - aren't we evil! */
  582.   filename_copy = g_strdup (filename);
  583.  
  584.   /* find the extension, boy! */
  585.   ext = strrchr (filename_copy, '.');
  586.  
  587.   while (TRUE)
  588.     {
  589.       if (!ext || ext[1] == '\0' || strchr (ext, '/'))
  590.     {
  591.       return NULL;
  592.     }
  593.       if (0 == g_strcasecmp (ext, ".xcfgz"))
  594.     {
  595.       return ".xcf";  /* we've found it */
  596.     }
  597.       if (0 != g_strcasecmp (ext,".gz"))
  598.     {
  599.       return ext;
  600.     }
  601.       else
  602.     {
  603.       /* we found ".gz" so strip it, loop back, and look again */
  604.       *ext = '\0';
  605.       ext = strrchr (filename_copy, '.');
  606.     }
  607.     }
  608. }
  609.